package com.yami.shop.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yami.shop.bean.app.dto.SkuDto;
import com.yami.shop.bean.model.*;
import com.yami.shop.bean.param.ProductParam;
import com.yami.shop.common.constants.CacheNames;
import com.yami.shop.common.enums.StatusEnum;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.i18n.I18nMessage;
import com.yami.shop.common.i18n.LanguageEnum;
import com.yami.shop.common.util.PageParam;
import com.yami.shop.common.util.PoiExcelUtil;
import com.yami.shop.common.util.RedisUtil;
import com.yami.shop.dao.SkuLangMapper;
import com.yami.shop.dao.SkuMapper;
import com.yami.shop.manager.impl.LangManager;
import com.yami.shop.service.SkuLangService;
import com.yami.shop.service.SkuService;
import com.yami.shop.service.SkuStockService;
import com.yami.shop.common.util.BeanUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.ss.usermodel.Sheet;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.stream.Collectors;

/**
 *
 * @author lgh on 2018/09/29.
 */
@Service
public class SkuServiceImpl extends ServiceImpl<SkuMapper, Sku> implements SkuService {

    @Autowired
    private SkuMapper skuMapper;
    @Autowired
    private SkuLangMapper skuLangMapper;
    @Autowired
    private SkuLangService skuLangService;
    @Autowired
    private SkuStockService skuStockService;
    @Autowired
    private LangManager langManager;


    private final static String[] SKU_EXPORT_COL_CN = {
            "商品编码", "商品名称", "规格", "商品库存", "商品价格"
    };

    private final static String[] SKU_EXPORT_COL_EN = {
            "Commodity Code","Commodity Name", "Specification","Inventory", "Price"
    };

    @Override
    @Cacheable(cacheNames = CacheNames.SKU_LIST_KEY,  key = "#prodId")
    public List<Sku> listSkuByProdId(Long prodId) {
        List<Sku> skuList = skuMapper.listByProdId(prodId);
        langManager.getSkuAndLang(skuList);
        if(CollectionUtils.isEmpty(skuList)){
            return null;
        }
        return skuList;
    }

    @Override
    public List<Sku> listSkuAndSkuNameByProdId(Long prodId) {
        SkuServiceImpl skuService = (SkuServiceImpl) AopContext.currentProxy();
        List<Sku> skuList = skuService.listSkuByProdId(prodId);
        langManager.handleSkuLang(skuList);
        return skuList;
    }

    @Override
    public List<Sku> listSkuAndSkuStock(Long prodId) {
        List<Sku> skuList = this.listSkuAndSkuStockAndLang(prodId);
        for (Sku sku : skuList) {
            sku.setSkuLangList(null);
        }
        return skuList;
    }

    @Override
    public List<Sku> listSkuAndSkuStockAndLang(Long prodId) {
        SkuServiceImpl skuService = (SkuServiceImpl) AopContext.currentProxy();
        List<Sku> skuList = skuService.listSkuByProdId(prodId);
        if (CollUtil.isEmpty(skuList)) {
            return skuList;
        }
        List<SkuStock> skuStocks = skuStockService.listSkuStockByProdId(prodId);
        Map<Long, SkuStock> skuStockMap = skuStocks.stream().collect(Collectors.toMap(SkuStock::getSkuId, s -> s));
        for (Sku sku : skuList) {
            if (!skuStockMap.containsKey(sku.getSkuId())) {
                continue;
            }
            // 插入库存信息
            SkuStock skuStock = skuStockMap.get(sku.getSkuId());
            sku.setStocks(skuStock.getStocks());
            sku.setActualStock(skuStock.getActualStock());
        }
        langManager.handleSkuAndLang(skuList);
        return skuList;
    }

    @Override
    public List<Sku> listPutOnSkuAndSkuStock(Long prodId) {
        List<Sku> skus = listSkuAndSkuStock(prodId);
        // 这个接口都是用户端在用，不需要返回完整的国际化信息
        for (Sku sku : skus) {
            sku.setSkuLangList(null);
        }
        return skus.stream().filter(sku -> Objects.equals(sku.getStatus(), StatusEnum.ENABLE.value())).collect(Collectors.toList());
    }

    @Override
    @Cacheable(cacheNames = CacheNames.SKU_KEY, key = "#skuId")
    public Sku getSkuBySkuId(Long skuId) {
        Sku sku = skuMapper.getSkuBySkuId(skuId);
        langManager.getSkuAndLang(sku);
        return sku;
    }

    @Override
    public Sku getSkuAndName(Long skuId) {
        SkuServiceImpl skuService = (SkuServiceImpl) AopContext.currentProxy();
        Sku sku = skuService.getSkuBySkuId(skuId);
        if (Objects.isNull(sku)) {
            return sku;
        }
        langManager.handleSkuAndLang(sku);
        return sku;
    }

    @Override
    public List<Sku> getSkuBySkuIds(List<Long> skuIds) {
        return skuMapper.getSkuBySkuIds(skuIds);
    }

    @Override
    public Sku getSkuListBySkuId(Long skuId) {
        Sku sku = skuMapper.getSkuListBySkuId(skuId);
        langManager.getSkuAndLang(sku);
        return sku;
    }

    @Override
    public void removeSkuCacheBySkuId(Long skuId, Long prodId) {
        List<String> keys = new ArrayList<>();
        // cacheNames = "sku"
        keys.add(CacheNames.SKU_KEY + CacheNames.UNION + skuId);
        // cacheNames = "skuList"
        keys.add(CacheNames.SKU_LIST_KEY + CacheNames.UNION + prodId);
        keys.add(CacheNames.SKU_LIST_KEY + CacheNames.UNION + prodId);
        // cacheNames = "listSkuStockByProdId",
        keys.add(CacheNames.LIST_SKU_STOCK_KEY + CacheNames.UNION + prodId);
        RedisUtil.del(keys);
    }

    @Override
    public void removeSkuStockCache(Long skuId, Long prodId) {
        List<String> keys = new ArrayList<>();
        // cacheNames = "sku"
        keys.add(CacheNames.SKU_KEY + CacheNames.UNION + skuId);
        // cacheNames = "listSkuStockByProdId",
        keys.add(CacheNames.LIST_SKU_STOCK_KEY + CacheNames.UNION + prodId);
        RedisUtil.del(keys);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateSku(Sku sku) {
        // 移除空字符串
        if (Objects.nonNull(sku) && StrUtil.isBlank(sku.getPic())) {
            sku.setPic(null);
        }
        sku.setUpdateTime(new Date());
        Integer changeStock = sku.getChangeStock();
        // 设置库存为null, 不更新库存
        Integer tempStock = sku.getStocks();
        // 更新sku,该方法不更新库存
        skuMapper.updateSkuById(sku);
        skuLangMapper.batchUpdateBySkuIdAndLang(sku.getSkuLangList());
        // 通过变化库存数更新库存
        if (Objects.nonNull(changeStock)) {
            Sku temp = getSKuAndStock(sku.getSkuId());
            if (changeStock != 0) {
//                if (tempStock - changeStock != temp.getStocks()) {
//                    throw new YamiShopBindException("yami.product.sku.invalid");
//                }
                skuStockService.updateSkuStocks(sku.getSkuId(), changeStock);
            }
//            else {
//                if (!Objects.equals(temp.getStocks(), tempStock)) {
//                    throw new YamiShopBindException("yami.product.sku.invalid");
//                }
//            }
        }
    }

    private Sku getSKuAndStock(Long skuId) {
        Sku sku = skuMapper.getSkuBySkuId(skuId);
        langManager.getSkuAndLang(sku);
        return sku;
    }

    @Override
    public void insertBatchAndLang(List<Sku> skuList) {
        // 移除空字符串
        for (Sku sku : skuList) {
            if (StrUtil.isBlank(sku.getPic()) && Objects.nonNull(sku)) {
                sku.setPic(null);
            }
        }
        skuMapper.insertBatchReturnId(skuList);
        // 保存语言表
        saveSkuLang(skuList);
        // 保存sku库存
        saveSkuStocks(skuList);
    }

    /**
     * 保存sku库存信息
     * @param skuList
     */
    private void saveSkuStocks(List<Sku> skuList) {
        List<SkuStock> skuStocks = new ArrayList<>();
        for (Sku sku : skuList) {
            SkuStock skuStock = new SkuStock();
            skuStock.setSkuId(sku.getSkuId());
            skuStock.setStocks(sku.getStocks());
            skuStock.setActualStock(sku.getStocks());
            skuStock.setLockStock(0);
            skuStock.setProdId(sku.getProdId());
            skuStocks.add(skuStock);
        }
        skuStockService.saveBatch(skuStocks);
    }

    @Override
    public List<String> listSkuByProdIds(List<Long> prodIds,Long disProdId) {
        if (CollectionUtils.isNotEmpty(prodIds)){
            return skuMapper.listSkuByProdIds(prodIds,disProdId);
        }
        return new ArrayList<>();
    }

    @Override
    public List<Sku> listSkuAndLangByProdIds(List<Long> prodIds) {
        List<Sku> skuList = skuMapper.listSkuAndLangByProdIds(prodIds);
        langManager.handleSkuAndLang(skuList);
        return skuList;
    }

    @Override
    public Sku getSkuByShopIdAndPartyCode(Long shopId, String partyCode) {
        Sku skuList = skuMapper.getSkuByPartyCode(shopId, partyCode);
        langManager.getProdNameToSku(skuList);
        return skuList;
    }

    @Override
    public IPage<StockBillLogItem> inquireStock(PageParam<StockBillLog> page, ProductParam productParam) {
        IPage<StockBillLogItem> stockBillLogItemIPage = skuMapper.inquireStock(page, productParam);
        langManager.getStockBillLogItem(stockBillLogItemIPage.getRecords());
        return stockBillLogItemIPage;
    }

    @Override
    public void exportSkuList(ProductParam product, HttpServletResponse response) {
        List<Sku> skuList = skuMapper.listExportSku(product);
        if (CollUtil.isEmpty(skuList)) {
            return;
        }
        langManager.getSkuAndLang(skuList);
        ExcelWriter writer = ExcelUtil.getBigWriter();
        final List<String> exportCol = Arrays.asList(
            Objects.equals(I18nMessage.getLang(), LanguageEnum.LANGUAGE_ZH_CN.getLang()) ? SKU_EXPORT_COL_CN : SKU_EXPORT_COL_EN
        );
        // 最大列数量
        int maxColNum = exportCol.size();

        Sheet sheet = writer.getSheet();
        // 设置列宽
        this.setColumnWidth(sheet);
        // 标题
        writer.merge(maxColNum - 1, Objects.equals(I18nMessage.getLang(), LanguageEnum.LANGUAGE_ZH_CN.getLang()) ? "商品库存信息整理" : "Commodity inventory information collation");
        // 列名
        writer.writeRow(exportCol);
        int rowIndex = 2;
        for (Sku sku : skuList) {
            int col = -1;
            // 商品编码
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++col, col, sku.getPartyCode());
            // 商品名称
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++col, col, sku.getProdName());
            // 规格
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++col, col, sku.getSkuName());
            // 商品库存
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++col, col, sku.getStocks());
            // 商品价格
            PoiExcelUtil.mergeIfNeed(writer, rowIndex, rowIndex, ++col, col, sku.getPrice());

            ++rowIndex;
        }
        PoiExcelUtil.writeExcel(response, writer);
    }

    @Override
    public int updateStocks(Sku sku) {
        return skuStockService.updateStocks(sku);
    }

    @Override
    public List<SkuDto> getProdDetailSkuInfo(Long prodId) {
        List<Sku> skuList = skuMapper.getProdDetailSkuInfo(prodId);
        if (CollUtil.isEmpty(skuList)) {
            return new ArrayList<>();
        }
        langManager.getSkuLang(skuList);
        return BeanUtil.mapAsList(skuList, SkuDto.class);
    }

    @Override
    public int updateSkuStocks(Long skuId, Integer stocks) {
        return skuStockService.updateSkuStocks(skuId, stocks);
    }


    private void setColumnWidth(Sheet sheet) {
        int index = -1;
        // 商品编码
        sheet.setColumnWidth(++index, 50 * 256);
        // 商品名称
        sheet.setColumnWidth(++index, 80 * 256);
        // 规格
        sheet.setColumnWidth(++index, 50 * 256);
        // 商品库存
        sheet.setColumnWidth(++index, 20 * 256);
        // 商品价格
        sheet.setColumnWidth(++index, 20 * 256);
    }

    @Override
    public IPage<Sku> pageSku(PageParam<Sku> page, ProductParam product) {
        IPage<Sku> skuPage = skuMapper.pageSku(page, product);
        langManager.getSkuLang(skuPage.getRecords());
        return skuPage;
    }

    private void saveSkuLang(List<Sku> skuList) {
        List<SkuLang> skuLangList = new ArrayList<>();
        for (Sku sku : skuList) {
            checkProdLang(sku.getSkuLangList());
            for (SkuLang skuLang : sku.getSkuLangList()) {
                skuLang.setSkuId(sku.getSkuId());
            }
            skuLangList.addAll(sku.getSkuLangList());
        }
        skuLangService.saveBatch(skuLangList);
    }

    private void checkProdLang(List<SkuLang> skuLangList) {
        Integer lang = langManager.getDefaultLang();
        boolean containsMaster = false;
        for (SkuLang skuLang : skuLangList) {
            if (Objects.equals(skuLang.getLang(), lang)) {
                containsMaster = true;
            }
        }
        if (!containsMaster) {
            throw new YamiShopBindException("语言数据已更新，请重新录入商品信息");
        }
    }
    @Override
    public List<Sku> listSkuBySKuIds(List<Long> skuIds) {
        if (CollUtil.isEmpty(skuIds)) {
            return new ArrayList<>();
        }
        return skuMapper.listSkuBySKuIds(skuIds);
    }
    @Override
    public List<Sku> listSkuByPartyCode(String partyCode) {
        return skuMapper.listSkuByPartyCode(partyCode);
    }

    @Override
    public Integer stockWarningCount(Long shopId) {
        return skuMapper.stockWarningCount(shopId);
    }
}
